{% extends "_base.html" %}


{% block content %}
<div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
    <ul class="layui-tab-title">
        <li class="layui-this">资源</li>
        <li>客户端</li>
    </ul>
    <div class="layui-tab-content">
        <div class="layui-tab-item layui-show">
            <!-- 资源状态列表 -->
            <div class="layui-card">
                <!-- <div class="layui-card-header">资源状态</div> -->
                <div class="layuui-card-header">
                    <p id="resource-search" style="display: inline-block; margin: 5px 10px;"><label>资源名称:<input type="text"></label><button type="button">搜索</button></p>
                    <p id="poll-intervaler" style="display: inline-block; margin: 5px 10px;"><button class="layui-btn layui-btn-normal">停止轮询</button> </p>
                </div>
                <div class="layui-card-body">
                    <table id="resource-list" lay-filter="resource-list"></table>
                </div>
            </div>

            <!-- 资源详情列表 -->
            <div class="layui-collapse">
                <div class="layui-colla-item">
                    <h2 class="layui-colla-title"><span id="resource-detail-name"></span>资源详情</h2>
                    <div class="layui-colla-content layui-show">
                        <table id="resource-peer-list" lay-filter="resource-peer-list"></table>
                        <div id="resource-chart" style="width: 100%;height: 200px;"></div>
                    </div>
                    <div style="padding: 10px">
                        <p>flags 含义:</p>
                        <strong>I</strong>: interesting
                        <strong>i</strong>: remote_interested
                        <strong>C</strong>: choked
                        <strong>c</strong>: remote_choked
                        <strong>e</strong>: supports_extensions 
                        <strong>l</strong>: local_connection
                    </div>
                </div>
            </div>

            <!-- 客户端列表 -->
            <div class="layui-collapse">
                <div class="layui-colla-item">
                    <h2 class="layui-colla-title"><span id="resource-detail-name"></span>客户端列表</h2>
                    <div class="layui-colla-content layui-show">
                        <table id="resource-client-list" lay-filter="resource-client-list"></table>
                    </div>
                </div>
            </div>
        </div>
        <div class="layui-tab-item">
            <div class="layui-card">
                <!-- <div class="layui-card-header">客户端</div> -->
                <div class="layui-card-body">
                    <table id="client-list" lay-filter="client-list"></table>
                </div>
            </div>
        </div>
    </div>
</div>


<div id="centrifugo-data" style="display: none">
    <i data="url">{{ url }}</i>
    <i data="user">{{ user }}</i>
    <i data="timestamp">{{ timestamp }}</i>
    <i data="info">{{ info }}</i>
    <i data="token">{{ token }}</i>
    <i data="channel">{{ channel }}</i>
</div>
{% endblock %}


{% block script %}
<script src="https://cdn.bootcss.com/echarts/4.1.0/echarts.min.js"></script>
<script src="/static/script/centrifuge.js"></script>
<script src="/static/script/inner_services.js"></script>

<!-- 列表右侧按钮栏 -->
<script type="text/html" id="resourceStautsButtonsTpl">
    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">查看</a>
    <a class="layui-btn layui-btn-xs" lay-event="distribution">分配</a>
    <!-- <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a> -->
</script>

<script>
layui.use(['laydate', 'laypage', 'layer', 'table', 'carousel', 'upload', 'element', 'laytpl', 'form', 'jquery'], function() {
    const laydate = layui.laydate,  //日期
        laypage = layui.laypage,    //分页
        layer = layui.layer,        //弹层
        table = layui.table         //表格
        laytpl = layui.laytpl,      //模板
        form = layui.form,
        $ = layui.jquery

    let clients = [];                   // 在线客户端列表
    let selectedResourceId = null;      // 当前选中资源(id)
    let selectedResourceInfoHash = '';  // 当前选中资源(info hash)
    let selectedResourceName = '';      // 当前选中资源(name)
    let selectedResourceClients = [];   // 当前选中资源对应的客户端列表
    let pollIntervaler = null;          // 轮询定时器

    // websocket
    const $centrifugoData = $('#centrifugo-data');
    const centrifuge = new Centrifuge({
        url: $centrifugoData.find('i[data=url]').text(),
        user: $centrifugoData.find('i[data=user]').text(),
        timestamp: $centrifugoData.find('i[data=timestamp]').text(),
        info: $centrifugoData.find('i[data=info]').text(),
        token: $centrifugoData.find('i[data=token]').text()
    });

    const channel = $centrifugoData.find('i[data=channel]').text();
    const callbacks = {
        "message": function(message) {
            console.log(message);
            const msgType = message.data['msg_type'];
            const clientId = message.client;
            const uid = message.uid;
            
            if (msgType == 'WHO_HAS_THIS_RESOURE_ANSWER') {
                const infoHash = message.data['info_hash'];
                const name = message.data['name']
                if (infoHash !== selectedResourceInfoHash || name !== selectedResourceName) {
                    return;
                }
                let isUpdate = false;
                for (item of selectedResourceClients) {
                    if (item.user == message.info.user) {
                        item.client_id = clientId;
                        item.progress = message.data.progress;
                        isUpdate = true;
                        break;
                    }
                }
                if (!isUpdate) {
                    selectedResourceClients.push({
                        client_id: clientId,
                        user: message.info.user,
                        name: message.info.default_info.name,
                        nat_type: message.info.default_info.nat_type,
                        external_ip: message.info.default_info.external_ip,
                        external_port: message.info.default_info.external_port,
                        client_version: message.info.default_info.client_version,
                        personnel_channel: message.info.default_info.personnel_channel,
                        state: message.data.state,
                        total_download: message.data.total_download,
                        total_upload: message.data.total_upload,
                        progress: message.data.progress,
                        peers: message.data.peers,
                    });
                }
                resourceClientListTable.reload({data: selectedResourceClients});
            }
        },
        "join": function(message) {
            // console.log(message);
            layer.msg('客户端"' + message.data.default_info.name +'"上线.');
            loadClientList();
        },
        "leave": function(message) {
            layer.msg('客户端"' + message.data.default_info.name +'"离线.');
            loadClientList();
        },
        "subscribe": function(context) {
            // console.log(context);
        },
        "error": function(errContext) {
            console.warn(err);
        },
        "unsubscribe": function(context) {
            console.warn(context);
        }
    }
    const subscription = centrifuge.subscribe(channel, callbacks);
    centrifuge.connect();

    resourceQuery = {};

    // 资源列表
    const resourceListTable = table.render({
        elem: '#resource-list',
        url: innerServices.resourcesAPI.url, //数据接口
        method: 'get',
        where: resourceQuery,
        request: {
            pageName: 'p_index',
            limitName: 'p_size'
        },
        response: {
            'statusName': 'code',
            'statusCode': 0,
            'msgName': 'codemsg',
            'countName': 'p_total',
            'dataName': 'data'
        },
        // height: 600,
        page: true,     //开启分页
        cols: [[ //表头
            {field: 'id', title: 'ID', width:80, align: 'center', sort: true, fixed: 'left'},
            {field: 'name', title: '资源名称', width:200, style: "color: #000"},
            {field: 'category', title: '分类', align: 'center', width:100},
            {field: 'info_hash', title: 'info-hash', width:140},
            {field: 'state', title: '状态', width:140, align: 'center', sort: true, templet: function (obj) {
                let color = '#000080';
                if (obj.state == 'seeding') {
                    color = '#ffa500';
                } else if (obj.state == 'stop') {
                    color = '#ff0000';
                }
                return '<span style="color: ' + color + ';">' + obj.state + '</span>';
            }},
            {field: 'size', title: '文件大小', align:'center', width:120, templet: function(obj) {
                const size = add_suffix(obj.size);
                return '<span>' + size + '</span>';
            }},
            {field: 'download_rates', title: '下载速度', align:'center', width:120, templet: function(obj) {
                const speed = add_suffix(obj.download_rates[obj.download_rates.length - 1]) + '/s';
                return '<span>' + speed + '</span>';
            }},
            {field: 'upload_rates', title: '上传速度', align:'center', width:120, templet: function(obj) {
                const speed = add_suffix(obj.upload_rates[obj.upload_rates.length - 1]) + '/s';
                return '<span>' + speed + '</span>';
            }},
            {field: 'total_download', title: '总下载量', align:'center', width:120, templet: function(obj) {
                const total = add_suffix(obj.total_download);
                return '<span>' + total + '</span>';
            }},
            {field: 'total_upload', title: '总上传量', align:'center', width:120, templet: function(obj) {
                const total = add_suffix(obj.total_upload);
                return '<span>' + total + '</span>';
            }},
            {field: 'create_time', title: '创建时间', align:'center', width:200, sort: true},
            {field: 'update_time', title: '更新时间', align:'center', width:200, sort: true},
            {fixed: 'right', align:'center', width:220, toolbar: '#resourceStautsButtonsTpl'}
        ]],
        done: function(res, curr, count){
            //如果是异步请求数据方式，res即为你接口返回的信息。
            //如果是直接赋值的方式，res即为：{data: [], count: 99} data为当前页数据、count为数据总长度
            // console.log(res);
            
            // //得到当前页码
            // console.log(curr); 
            
            // //得到数据总量
            // console.log(count);
        }
    });

    // 客户端列表
    const clientListTable = table.render({
        elem: '#client-list',
        cols: [[ //表头
            {field: 'name', title: '名称(HostName)', width:320, fixed: 'left'},
            {field: 'nat_type', title: 'Nat类型', align:'center', width:240, templet: function (obj) {
                    const text = obj.nat_type;
                    const natToColor = {
                        'Open Internet': '#3498DB',
                        'Full Cone': '#58D68D',
                        'Symmetric UDP Firewall': '#F4D03F',
                        'Restric NAT': '#F8C471',
                        'Restric Port NAT': '#F0B27A',
                        'Symmetric NAT': '#DC7633',
                        'Blocked': '#C0392B',
                        'Unknown': '#888888',
                    }
                    return '<span style="color: ' + natToColor[text] + ';">' + text + '</span>';
                }
            },
            {title: '地址', align:'center', width:240, templet: function (obj) {
                return '<span>' + obj.external_ip + ':' + obj.external_port + '</span>';
            }},
            {field: 'is_online', title: '状态', align:'center', width: 165, templet: function(obj) {
                let color = null;
                let text = null;
                if (obj.is_online == true) {
                    color = '#a9d764';
                    text = '在线';
                } else if (obj.state == 'stop') {
                    color = '#888888';
                    text = '离线';
                }
                return '<span style="color: ' + color + ';">' + text + '</span>';
            }},
            {field: 'personnel_channel', title: '专用ws频道', align: 'center', width:320},
            {field: 'client_version', width: 165, title:'客户端版本'},
            // {fixed: 'right', width: 165, align:'center', toolbar: '#resourceStautsButtonsTpl'}
        ]],
        data: [],
        page: true,
        limt: 10
    });

    // 选中资源的peer列表
    const resourcePeerListTable = table.render({
        elem: '#resource-peer-list',
        cols: [[ //表头
            {field: 'address', title: '远端', width:180, fixed: 'left'},
            {field: 'down_speed', title: '下载速度', align:'center', width:120, templet: function(obj) {
                const speed = add_suffix(obj.down_speed) + '/s';
                return '<span>' + speed + '</span>';
            }},
            {field: 'up_speed', title: '上传速度', align:'center', width:120, templet: function(obj) {
                const speed = add_suffix(obj.up_speed) + '/s';
                return '<span>' + speed + '</span>';
            }},
            {field: 'total_download', title: '总下载量', align:'center', width:120, templet: function(obj) {
                const total = add_suffix(obj.total_download);
                return '<span>' + total + '</span>';
            }},
            {field: 'total_upload', title: '总上传量', align:'center', width:120, templet: function(obj) {
                const total = add_suffix(obj.total_upload);
                return '<span>' + total + '</span>';
            }},
            {field: 'download_queue_length', title: '下载队列长度', width:130},
            {field: 'upload_queue_length', title: '上传队列长度', width:130},
            {field: 'flags', width: 165, title:'flags', templet: function(obj) {
                // 参考: https://www.libtorrent.org/reference-Core.html#peer_flags_t
                const PeerFlagsEnum = {
                    'interesting': 1,
                    'choked': 2,
                    'remote_interested': 4,
                    'remote_choked': 8,
                    'supports_extensions': 16,
                    'local_connection': 32,
                }
                let text = '';

                text += obj.flags & PeerFlagsEnum.interesting ? 'I' : '.';
                text += obj.flags & PeerFlagsEnum.choked ? 'C' : '.';
                text += obj.flags & PeerFlagsEnum.remote_interested ? 'i' : '.';
                text += obj.flags & PeerFlagsEnum.remote_choked ? 'c' : '.';
                text += obj.flags & PeerFlagsEnum.supports_extensions ? 'e' : '.';
                text += obj.flags & PeerFlagsEnum.local_connection ? 'l' : '.';

                return '<span style="color: #0040FF; font-size: 700">' + text + '</span>';
            }},
            {field: 'client_version', width: 165, title:'客户端版本'},
        ]],
        data: [],
        page: true,
        limt: 5
    });

    // 选中资源的client列表
    const resourceClientListTable = table.render({
        elem: '#resource-client-list',
        cols: [[ //表头
            {field: 'name', title: '名称', width:320, fixed: 'left'},
            {field: 'nat_type', title: 'Nat类型', align:'center', width:240, templet: function (obj) {
                const text = obj.nat_type;
                const natToColor = {
                    'Open Internet': '#3498DB',
                    'Full Cone': '#58D68D',
                    'Symmetric UDP Firewall': '#F4D03F',
                    'Restric NAT': '#F8C471',
                    'Restric Port NAT': '#F0B27A',
                    'Symmetric NAT': '#DC7633',
                    'Blocked': '#C0392B',
                    'Unknown': '#888888',
                }
                return '<span style="color: ' + natToColor[text] + ';">' + text + '</span>';
            }},
            {title: '地址', align:'center', width:240, templet: function (obj) {
                return '<span>' + obj.external_ip + ':' + obj.external_port + '</span>';
            }},
            {field: 'state', title: '状态', width:140, sort: true, templet: function (obj) {
                let color = '#000080';
                if (obj.state == 'seeding') {
                    color = '#ffa500';
                } else if (obj.state == 'stop') {
                    color = '#ff0000';
                }
                return '<span style="color: ' + color + ';">' + obj.state + '</span>';
            }},
            {field: 'progress', title: '完成度', align:'center', width: 165, templet: function(obj) {
                return '<span style="font-weight: 700;">' + obj.progress * 100 + '%</span>';
            }},
            {field: 'total_download', title: '总下载量', align:'center', width:120, templet: function(obj) {
                const total = add_suffix(obj.total_download);
                return '<span>' + total + '</span>';
            }},
            {field: 'total_upload', title: '总上传量', align:'center', width:120, templet: function(obj) {
                const total = add_suffix(obj.total_upload);
                return '<span>' + total + '</span>';
            }},
            {field: 'peers', title: '链接节点', align:'center', width:400, templet: function(obj) {
                const l = [];
                for (let peer of obj.peers) {
                    l.push(peer.addr)
                }
                return '<strong>(' + l.length + ')</strong> <span>'  + l + '</span>';
            }},
            {field: 'client_version', width: 165, title:'客户端版本'},
        ]],
        data: [],
        page: true,
        limt: 10
    });
    
    // 资源详情中的统计图
    const resourceChart = (function() {
        let resourceChart = echarts.init(document.getElementById('resource-chart'));
        const option = {
            // title: {
            //     text: ''
            // },
            tooltip: {
                trigger: 'axis'
            },
            legend: {
                data: ['下载速度', '上传速度']
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
            },
            toolbox: {
                feature: {
                    saveAsImage: {}
                }
            },
            xAxis: {
                show: false,
                type: 'category',
                boundaryGap: false,
                data: []
            },
            yAxis: {
                type: 'value'
            },
            series: [
                {
                    name: '下载速度',
                    type: 'line',
                    stack: '总量',
                    data: []
                },
                {
                    name: '上传速度',
                    type: 'line',
                    stack: '总量',
                    data: []
                }
            ]
        };
        resourceChart.setOption(option);
        return resourceChart;
    })();

    /**
     * 加载客户端列表
     */
    function loadClientList() {
        subscription.presence().then(function (message) {
            // console.log(message);
            clients = [];
            window.res = message.data
            for (let client_id in message.data) {
                const item = message.data[client_id];
                if (item.user.startsWith('browser')) {
                    continue;
                }
                console.log(item)
                clients.push({
                    client_id: item.client,
                    user: item.user,
                    name: item.default_info.name,
                    nat_type: item.default_info.nat_type,
                    external_ip: item.default_info.external_ip,
                    external_port: item.default_info.external_port,
                    client_version: item.default_info.client_version,
                    personnel_channel: item.default_info.personnel_channel,
                    is_online: true
                });
            }
            clientListTable.reload({ data: clients });
        }, function (error) {
            console.error(error);
            layer.msg('获取客户端在线列表信息错误!');
        });
    }

    /**
     * 加载资源详情
     */
    function loadResourceDetail(selectedResourceId) {
        innerServices.resourcesAPI.getOne(selectedResourceId, (data) => {
            $('#resource-detail-name').text(data.name + ' (' + add_suffix(data.size) + ')');

            // 远端链接列表
            resourcePeerListTable.reload({
                data: data.peers,
            });

            // 统计图
            resourceChart.setOption({
                series: [
                    {
                        name: '下载速度',
                        type: 'line',
                        stack: '总量',
                        smooth: true,
                        data: data.upload_rates,
                    },
                    {
                        name: '上传速度',
                        type: 'line',
                        stack: '总量',
                        smooth: true,
                        data: data.download_rates,
                    }
                ]
            });
        }, (error) => {
            console.error(error);
            layer.msg('获取详情失败!');
        });
    }

    // 资源搜索
    $('#resource-search button').on('click', function () {
        let nameLike = $('#resource-search input').val().trim();
        if (!nameLike) {
            delete resourceQuery['nameLike'];
        } else {
            resourceQuery['nameLike'] = nameLike;
        }
        resourceListTable.reload({ page: { curr: resourceListTable.config.page.curr }, where: resourceQuery });
    });

    // 资源列表 右侧按钮
    table.on('tool(resource-list)', function (obj) {
        // 查看详情
        if (obj.event === 'detail') {
            selectedResourceId = obj.data.id;
            selectedResourceInfoHash = obj.data.info_hash;
            selectedResourceName = obj.data.name;
            loadResourceDetail(selectedResourceId);

            selectedResourceClients = [];
            resourceClientListTable.reload({ data: selectedResourceClients });
            // 资源client列表
            subscription.publish({
                msg_type: 'WHO_HAS_THIS_RESOURE',
                name: selectedResourceName,
                info_hash: selectedResourceInfoHash,
            }).then(function(message) {
                console.log(message);
            });

        // 分配下载
        } else if (obj.event === 'distribution') {
            const $content = $('<div><form class="checkbox-container"></form></div>');
            for (client of clients) {
                $content.find('.checkbox-container').append('<label style="display: block; margin: 10px 32px;"><input type="checkbox" name="select-client" value="' + client.user  + '">' + client.name + '</label>')
            }
            layer.open({
                type: 1,
                title: '分配客户端下载资源: "' + obj.data.name + '"',
                shade: 0.8,
                shadeClose: true, //开启遮罩关闭
                closeBtn: false,
                area: '500px',
                id: 'LAY_client_select', //设定一个id，防止重复弹出
                btn: ['分配下载', '取消'],
                btnAlign: 'c',
                moveType: 1, //拖拽模式，0或者1
                content: $content.html(),
                yes: function(index, $layero) {
                    // console.log(index);
                    // console.log($layero);
                    // window.$layero = $layero;
                    const data = $layero.find('form').serializeArray();
                    users = []
                    for (let item of data) {
                        if (item.name !== 'select-client') {
                            continue;
                        }
                        const user = item.value;
                        users.push(user);
                    }
                    innerServices.clientsNotifyAPI.notify('resource-download', {
                        resource_id: obj.data.id,
                        users: JSON.stringify(users)
                    }, () => {
                        layer.msg('分配成功');
                        return true;
                    }, (error) => {
                        console.error(error);
                        layer.msg('分配失败!');
                    })
                },
                // btn2: function(index, layero) {
                    
                // }
            });
        }
    });

    // 定时轮询
    $('#poll-intervaler button').on('click', function() {
        if (pollIntervaler) {
            clearInterval(pollIntervaler);
            pollIntervaler = null;
            $(this).text('开启轮询');
        } else {
            // 定时任务
            pollIntervaler = setInterval(function () {
                // console.log(resourceListTable.config.page.curr)
                resourceListTable.reload({ page: { curr: resourceListTable.config.page.curr }, where: resourceQuery });

                if (selectedResourceId) {
                    loadResourceDetail(selectedResourceId);

                    // 资源client列表
                    subscription.publish({
                        msg_type: 'WHO_HAS_THIS_RESOURE',
                        name: selectedResourceName,
                        info_hash: selectedResourceInfoHash,
                    }).then(function(message) {
                        console.log(message);
                    });
                }
            }, 5000);
            $(this).text('停止轮询');
        }
    });
    $('#poll-intervaler button').click();
});
</script>  
{% endblock %}
